package com.ychen.nttey.chat;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import org.apache.log4j.Logger;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * @author chenyuliao
 * @version 1.0
 * @description
 * @date 2021/1/10 22:16
 */
public class NettyClient {

    private final Logger logger = Logger.getLogger(this.getClass());

    private final ConcurrentMap<String /* addr */, Channel> channelTables = new ConcurrentHashMap<>();

    private final Bootstrap bootstrap = new Bootstrap();

    public void start() {
        EventLoopGroup eventExecutors = new NioEventLoopGroup();
        try {
            this.bootstrap
                    .group(eventExecutors)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast("decode", new StringDecoder());
                            pipeline.addLast("encode", new StringEncoder());
                            pipeline.addLast(new GroupChatClientHandler());
                        }
                    });

        } finally {
            eventExecutors.shutdownGracefully();
        }
    }


    public void sendMessage(String inetHost, int inetPort, String message) throws InterruptedException {
        Channel channel = this.getAndCreateChannel(inetHost, inetPort);
        if (channel != null && channel.isActive()) {
            channel.writeAndFlush(message).addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture f) throws Exception {
                    if (!f.isSuccess()) {
                        logger.warn("send a request command to channel <" + channel.remoteAddress() + "> failed.");
                    }
                }
            });
        } else {
            logger.warn("sendMessage 没有找到相应的channel");
        }
    }

    private String getAddr(String inetHost, int inetPort) {
        return inetHost + ":" + inetPort;
    }


    private Channel getAndCreateChannel(String inetHost, int inetPort) {
        if (null == inetHost) {
            throw new RuntimeException("地址不能为空");
        }
        Channel channel = this.channelTables.get(getAddr(inetHost, inetPort));
        if (channel != null) {
            return channel;
        }
        return this.createChannel(inetHost, inetPort);
    }

    private Channel createChannel(String inetHost, int inetPort) {
        String addr = getAddr(inetHost, inetPort);
        ChannelFuture channelFuture = null;
        try {
            channelFuture = this.bootstrap.connect(inetHost, inetPort).sync();
            Channel channel = channelFuture.channel();
            this.channelTables.putIfAbsent(addr, channel);
            return channel;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }


}
